package com.llb666.shop.user.api.controller;

import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.llb666.common.core.constant.AppConstants;
import com.llb666.common.core.exception.AppException;
import com.llb666.common.core.utils.cookie.CookieUtils;
import com.llb666.common.core.utils.id.snow.SnowFlakeUtil;
import com.llb666.common.core.utils.id.uuid.IdUtils;
import com.llb666.common.core.web.error.CommonError;
import com.llb666.common.core.web.response.BaseResponse;
import com.llb666.common.core.web.response.DataResponse;
import com.llb666.common.redis.service.RedisService;
import com.llb666.shop.cart.domain.entity.ShopCartBo;
import com.llb666.shop.user.api.dto.UserLoginDto;
import com.llb666.shop.user.api.dto.UserRegisterDto;
import com.llb666.shop.user.api.vo.UsersVo;
import com.llb666.shop.user.api.vo.auth.Account;
import com.llb666.shop.user.api.vo.auth.AuthResponse;
import com.llb666.shop.user.api.vo.auth.ResponseCode;
import com.llb666.shop.user.application.AuthApplication;
import com.llb666.shop.user.domain.entity.Users;
import com.llb666.shop.user.domain.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * 用户服务表示层.
 *
 * @author llb66
 */
@Slf4j
@RestController
@Tag(name = "用户注册登录接口")
@RequestMapping("/passport")
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class UsersController {

    private static final String HEADER_USER_TOKEN = "headerUserToken";
    private static final String HEADER_USER_REFRESH_TOKEN = "headerUserRefreshToken";
    private static final String HEADER_USER_ID = "headerUserId";

    private final AuthApplication authApplication;
    private final UserService userService;
    private final RedisService redisService;

    @Operation(summary = "判断用户名称是否存在")
    @GetMapping("/usernameIsExist")
    public BaseResponse usernameIsExist(@Parameter(description = "用户名") @RequestParam String username) {
        //1.判空
        if (StringUtils.isBlank(username)) {
            throw new AppException(CommonError.RequestParamsInvalid, "用户名不能为空！");
        }
        boolean isExist = userService.queryUsernameIsExist(username);
        if (isExist) {
            throw new AppException(CommonError.RequestParamsInvalid, "用户名已经存在！");
        }
        return BaseResponse.ok();
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public DataResponse<UsersVo> login(@RequestBody UserLoginDto userDto,
                                       HttpServletRequest request,
                                       HttpServletResponse response) throws Exception {
        String username = userDto.getUsername();
        String password = userDto.getPassword();
        //1.判空
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            throw new AppException(CommonError.NeedAuthentication, "用户名和密码不能为空！");
        }
        Users result = userService.login(UserLoginDto.Converter.INSTANCE.dto2do(userDto));
        //实现用户redis会话:生成用户token，存入redis会话
//        String uniqueToken = UUID.randomUUID().toString().trim();
//        redisService.setCacheObject(AppConstants.REDIS_USER_TOKEN + ":" + result.getId(), uniqueToken);
        AuthResponse token = authApplication.token(result.getId());
        if (!ResponseCode.SUCCESS.getCode().equals(token.getCode())) {
            log.error("{}生成token失败", result.getUsername());
            throw new AppException(CommonError.NeedAuthentication, "生成token失败！");
        }
        UsersVo usersVo = UsersVo.Converter.INSTANCE.do2Vo(result);
        usersVo.setUserUniqueToken(token.getAccount().getToken());
        usersVo.setUserRefreshToken(token.getAccount().getRefreshToken());
        // 将token添加到header
        addAuthHeader(response, token.getAccount());
        //3.缓存
        CookieUtils.setCookie(request, response, "user", JSONObject.toJSONString(usersVo), true);
        // 同步购物车数据
        syncShopCartData(result.getId(), request, response);
        return DataResponse.ok(usersVo);
    }

    @Operation(summary = "注册")
    @PostMapping("/regist")
    public BaseResponse register(@RequestBody UserRegisterDto userDto,
                                 HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {
        String username = userDto.getUsername();
        String password = userDto.getPassword();
        String confirmPassword = userDto.getConfirmPassword();
        //1.判空.
        if (StringUtils.isBlank(username)) {
            throw new AppException(CommonError.RegisterInvalid, "用户名不能为空！");
        }
        if (StringUtils.isBlank(password)) {
            throw new AppException(CommonError.RegisterInvalid, "密码不能为空！");
        }
        if (StringUtils.isBlank(confirmPassword)) {
            throw new AppException(CommonError.RegisterInvalid, "请再次确认您的密码！");
        }
        //2.两次输入的密码应该保持一致.
        if (!StringUtils.equals(password, confirmPassword)) {
            throw new AppException(CommonError.RegisterInvalid, "两次密码输入不一致！");
        }
//        String userId = String.valueOf(SnowFlakeUtil.nextId());
        Users user = userService.register(UserRegisterDto.covert(null, userDto));
        //实现用户redis会话:生成用户token，存入redis会话.
//        UsersVo usersVo = UsersVo.Converter.INSTANCE.do2Vo(user);
        //5.缓存.
//        CookieUtils.setCookie(request, response, "user", JSONObject.toJSONString(usersVo), true);
        // 同步购物车数据.
//        syncShopCartData(user.getId(), request, response);
        return DataResponse.ok();
    }

    @Operation(summary = "退出登录")
    @PostMapping("/logout")
    public BaseResponse logout(@Parameter(description = "用户Id") @RequestParam String userId, HttpServletRequest request,
                               HttpServletResponse response) {
        Account account = Account.builder()
                .token(request.getHeader(HEADER_USER_TOKEN))
                .refreshToken(request.getHeader(HEADER_USER_REFRESH_TOKEN))
                .userId(userId)
                .build();
        authApplication.delete(account);
        // 清除cookie.
        CookieUtils.deleteCookie(request, response, "user");
        // 清除购物车.
        CookieUtils.deleteCookie(request, response, AppConstants.SHOP_MALL_CART);
        return BaseResponse.ok();
    }

    /**
     * 注册,登录成功后，同步cookie和redis中的购物车数据.
     */
    private void syncShopCartData(String userId, HttpServletRequest request,
                                  HttpServletResponse response) {
        /*
         * 1. redis中无数据，如果cookie中的购物车为空，那么这个时候不做任何处理
         *                 如果cookie中的购物车不为空，此时直接放入redis中
         * 2. redis中有数据，如果cookie中的购物车为空，那么直接把redis的购物车覆盖本地cookie
         *                 如果cookie中的购物车不为空，
         *                 如果cookie中的某个商品在redis中存在，
         *                      则以cookie为主，删除redis中的，
         *                      把cookie中的商品直接覆盖redis中（参考京东）
         * 3. 同步到redis中去了以后，覆盖本地cookie购物车的数据，保证本地购物车的数据是同步最新的
         */

        // 从redis中获取购物车
        String shopCartJsonRedis = redisService.getCacheObject(AppConstants.SHOP_MALL_CART + ":" + userId);
        // 从cookie中获取购物车
        String shopCartStrCookie = CookieUtils.getCookieValue(request, AppConstants.SHOP_MALL_CART, true);

        if (StringUtils.isBlank(shopCartJsonRedis)) {
            // 1.redis为空，cookie不为空，直接把cookie中的数据放入redis.
            if (StringUtils.isNotBlank(shopCartStrCookie)) {
                redisService.setCacheObject(AppConstants.SHOP_MALL_CART + ":" + userId, shopCartStrCookie);
            }
        } else {
            // 2. redis不为空，cookie不为空，合并cookie和redis中购物车的商品数据（同一商品则覆盖redis）.
            if (StringUtils.isNotBlank(shopCartStrCookie)) {
                /*
                 * 1. 已经存在的，把cookie中对应的数量，覆盖redis（参考京东）
                 * 2. 该项商品标记为待删除，统一放入一个待删除的list
                 * 3. 从cookie中清理所有的待删除list
                 * 4. 合并redis和cookie中的数据
                 * 5. 更新到redis和cookie中
                 */
                List<ShopCartBo> shopCartListRedis = JSON.parseArray(shopCartJsonRedis, ShopCartBo.class);
                List<ShopCartBo> shopCartListCookie = JSON.parseArray(shopCartStrCookie, ShopCartBo.class);
                // 定义一个待删除list
                List<ShopCartBo> pendingDeleteList = new ArrayList<>();
                for (ShopCartBo redisShopCart : shopCartListRedis) {
                    String redisSpecId = redisShopCart.getSpecId();
                    for (ShopCartBo cookieShopCart : shopCartListCookie) {
                        String cookieSpecId = cookieShopCart.getSpecId();
                        if (redisSpecId.equals(cookieSpecId)) {
                            // 覆盖购买数量，不累加，参考京东
                            redisShopCart.setBuyCounts(cookieShopCart.getBuyCounts());
                            // 把cookieShopCart放入待删除列表，用于最后的删除与合并
                            pendingDeleteList.add(cookieShopCart);
                        }
                    }
                }
                // 从现有cookie中删除对应的覆盖过的商品数据
                shopCartListCookie.removeAll(pendingDeleteList);
                // 合并两个list
                shopCartListRedis.addAll(shopCartListCookie);
                // 更新到redis和cookie
                CookieUtils.setCookie(request, response, AppConstants.SHOP_MALL_CART, JSON.toJSONString(shopCartListRedis), true);
                redisService.setCacheObject(AppConstants.SHOP_MALL_CART + ":" + userId, JSON.toJSONString(shopCartListRedis));
            } else {
                // 3.redis不为空，cookie为空，直接把redis覆盖cookie
                CookieUtils.setCookie(request, response, AppConstants.SHOP_MALL_CART, shopCartJsonRedis, true);
            }
        }
    }

    private void addAuthHeader(HttpServletResponse response, Account account) {
        response.setHeader(HEADER_USER_TOKEN, account.getToken());
        response.setHeader(HEADER_USER_REFRESH_TOKEN, account.getRefreshToken());
        response.setHeader(HEADER_USER_ID, account.getUserId());
        // 一天就过期
        Calendar expTime = Calendar.getInstance();
        expTime.add(Calendar.DAY_OF_MONTH, 1);
        response.setHeader("token-expired-time", expTime.getTimeInMillis() + "");
    }
}
